#coding:utf-8
#python3.8
__AUTHOR__ = "wangyong"

import os
import time

class SocketBit(object):
    '''
    1、读入二进制位图并格式化位图
    2、读入8583报文体报文，报文内容不能包含换行
    3、从本目录读取银联规范8583.conf
    '''
    def __init__(self):
        '''
        初始化
        '''
        self.conf_dir = os.path.dirname(__file__)
        self.conf_file = self.conf_dir + "\\8583.conf"
        # print(self.conf_file)
        self.word_bit = ""
        self.word_msgreq = ""

    def _convert_to_bin(self,data):
        '''
        16进制转二进制
        :param data:
        :return:
        '''
        if " " in data:     #去空格
            data = data.replace(" ", "")
        if "\\x" in data:       #去\\x
            data = data.replace("\\x", "")
        print("处理后的十六进制位图：【%s】"% data)
        bina = bin(int(data,16))        #转二进制bin
        return bina[2:]         #切片，从第二位开始取值

    def _convert_to_str(self,request_data):
        '''
        十六进制转字符串，注意报文中的空格以及\\x需要去除
        :param request_data:
        :return:
        '''
        if " " in request_data:     #去空格
            request_data = request_data.replace(" ","")
        if "\\x" in request_data:   #去转义\
            request_data = request_data.encode("utf-8").decode("unicode_escape")  # 此处必须使用unicode_escape取内存值，否则单反斜杠会被转义后赋值
        stra = str(request_data)
        return str(stra)

    def _bit_map(self):
        '''
        读入二进制位图并做格式化
        f2 3e 40 81 8a d0 80 98 00 00      00 00 10 00 00 20
        \xf2\x3e\x40\x81\x8a\xd0\x80\x98\x00\x00\x00\x00\x10\x00\x00\x20
        f23e40818ad080980000000010000020
        :return:
        '''
        while len(self.word_bit) == 0:
            self.word_bit = input("请输入位图，二进制、十六进制均可:\n")     #python2使用raw_input

        #判断是否为16进制
        if ("f" in self.word_bit) or ("e" in self.word_bit)  or ("2" in self.word_bit):
            print("您输入的位图为16进制，后台正在努力转成二进制位图，请稍后。。。。")
            time.sleep(1)
            self.word_bit = self._convert_to_bin(self.word_bit)     #十六进制转二进制
            print("生成的二进制位图：【%s】"%(self.word_bit))

        #获取二进制1位置
        li = []
        for i,j in enumerate(self.word_bit,1):
            if j=='1':
                li.append(i)
        #判断第一位是否为1
        if li[0] == 1:
            print("位图第一位为1，则域66到128有值")
        word = str(list(li))
        print("生成8583各域:\n%s"%(word))
        # 过滤生成的位图域信息
        word = word.replace(' ','')     #替换空格
        word = word.replace('[','')     #替换[
        word = word.replace(']','')     #替换]
        self.word_bitmap = word.split(',')     #过滤掉逗号
        #print(self.word_bitmap)
        return self.word_bitmap

    def _request(self):
        '''
        读入8583报文体，上送16进制、十进制均可
        \x31\x36\x36\x32\x32\x36\x33\x30\x32\x36
        16622630261049471
        :return:
        '''
        while len(self.word_msgreq) == 0:
            self.word_msgreq = input("请输入8583报文送十六进制、十进制均可（无8583报文头、无换行）:\n")

        #判断是否为8583报文
        if ("\\x30" in self.word_msgreq) or ("\\x31" in self.word_msgreq):
            print("请求报文为十六进制，正在转成十进制")
            self.word_msgreq = self._convert_to_str(self.word_msgreq)
            print("生成的十进制请求报文：【%s】"%(self.word_msgreq))
        #print(self.word_msg)
        return self.word_msgreq

    def _bank_rule(self):
        '''
        读入银联规范接口文件8583.conf
        :return:
        '''
        f = open(self.conf_file, "r", encoding="utf-8")
        date = f.read()
        f.close()
        self.bank_area = date.split('\n')
        #print(self.bank_area)
        return self.bank_area

    def _write_text(self):
        filen = self.conf_dir + "/result/8583_Result_"
        writetxt = input("请输入要保存的文件名称：").encode("utf-8").decode("unicode_escape")
        filename = filen + writetxt + ".txt"
        fw = open(filename, "a", encoding="utf-8")
        print("8583报文解析后文件名：" + filename)
        return fw

    def main(self):
        word_bitmap = self._bit_map()
        word_reqmsg = self._request()
        bank_area = self._bank_rule()
        write_nanl = self._write_text()
        print("---------8583报文解析如下--------------")
        for item in word_bitmap:
            for item_table in bank_area:
                info = item_table.split('\t')   #tab键分割银联规范接口
                #判断十进制位图是否存在于银联接口域中
                if item == info[0]:
                    #print(info[0])
                    #print(info[2])
                    if '/' in info[2]:
                        #判断变长，目前银联最大3位变长
                        len_list = info[2].split('/')
                        if len_list[0] == "2" :
                            str_var = "LLVAR 2位变长"
                        elif len_list[0] == "3" :
                            str_var = "LLLVAR 3位变长"
                        else:
                            str_var = "LLLLVAR 4位变长"
                        #对报文进行切片，比如："16" "6226300119173747"       //域2   主账号   2/19   LLVAR 2位变长
                        len_int = int( len_list[0] )     #获取变长域数据长度2
                        lens = int(word_reqmsg[0:len_int])        #获取变长域数据长度16
                        #获取字段值
                        word_reqmsg = word_reqmsg[len_int:]
                        word1 = word_reqmsg[0:lens]
                        # 打印解析后报文到控制台
                        # 以下判断获取变长长度，不够变量长度的前面补0
                        if len(str(lens)) == 1 and len_int == 2:        #2位变长，如果只获取到1位长度，长度前补0，比如长度2，前面补0，即02
                            lens_ll = str(0) + str(lens)
                            print('"', lens_ll, '"', '"', word1, '"', " ", " ", " ", "//域" + item, " ", info[1],
                                  " ", info[2], " ", str_var)
                        elif len(str(lens)) == 1 and len_int == 3 :    #3位变长，如果只获取到1位长度，长度前补00，比如长度2，前面补00，即002
                            lens_ll = str(0) + str(0) + str(lens)
                            print('"', lens_ll, '"', '"', word1, '"', " ", " ", " ", "//域" + item, " ", info[1],
                                  " ", info[2], " ", str_var)
                        elif len(str(lens)) == 2 and len_int == 3:    #3位变长，如果只获取到2位长度，长度前补0，比如长度14，前面补0，即014
                            lens_ll = str(0) + str(lens)
                            print('"', lens_ll, '"', '"', word1, '"', " ", " ", " ", "//域" + item, " ", info[1],
                              " ", info[2], " ", str_var)
                        elif  len(str(lens)) == 2 and len_int == 2:    #2位变长，域获取到的长度位数一致，则补改变变量长度
                            print('"', lens, '"', '"', word1, '"', " ", " ", " ", "//域" + item, " ", info[1],
                                  " ", info[2], " ", str_var)
                        # 写入到本件格式，注意2位变长和3位变长长度，不足两位或者三位的前面补0
                        #write_nanl.write("b" + item + " " + info[1] + " " + str_var + " " + info[2] + " " + '[' + str(lens) + ']' + " " + '[' + word1 + ']' + "\n")
                        write_nanl.write('"' + str(lens) + '"' + '"' + word1 + '"' + " " + " " + " " + "//域" + item + " " + info[1] + " " + info[2] + " " + str_var + "\n")
                        word_reqmsg = word_reqmsg[lens:]
                    elif '/' not in info[2]:       #定长
                        len1 = int(info[2])
                        word1 = word_reqmsg[0:len1]
                        str_var = "DATA 定长"
                        #打印解析后报文到控制台
                        #print("b"+item," ",info[1]," ",str_var," ",info[2]," ",'[',len1,']'," ",word1)
                        print('"', word1, '"', " ", " ", " ", "//域" + item, " ", info[1]," ", info[2] ," ", str_var)    #loadrunner 8583域定长使用
                        # 写入到本件格式
                        #write_nanl.write("b" + item + " " + info[1] + " " + str_var + " " + info[2] + " " + '[' + str(len1) + ']' + " " + '[' + word1 + ']' + "\n")
                        write_nanl.write('"' + str(len1) + '"' + '"' + word1 + '"' + " " + " " + " " + "//域" + item + " " + info[1]  + " " + info[2] + " " + str_var + "\n")
                        word_reqmsg = word_reqmsg[len1:]
        write_nanl.close()      #关闭写句柄

if __name__ == "__main__":
    socket = SocketBit()
    socket.main()
